  <!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="generator" content="pandoc" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <title>GTK 4 tutorial</title>
    <style>
      code{white-space: pre-wrap;}
      span.smallcaps{font-variant: small-caps;}
      span.underline{text-decoration: underline;}
      div.column{display: inline-block; vertical-align: top; width: 50%;}
      div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
      ul.task-list{list-style: none;}
      pre{overflow: visible;}
      pre > code.sourceCode { white-space: pre; position: relative; }
      pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
      pre > code.sourceCode > span:empty { height: 1.2em; }
      code.sourceCode > span { color: inherit; text-decoration: inherit; }
      div.sourceCode { margin: 1em 0; }
      pre.sourceCode { margin: 0; }
      @media screen {
      div.sourceCode { overflow: auto; }
      }
      @media print {
      pre > code.sourceCode { white-space: pre-wrap; }
      pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
      }
      pre.numberSource code
        { counter-reset: source-line 0; }
      pre.numberSource code > span
        { position: relative; left: -4em; counter-increment: source-line; }
      pre.numberSource code > span > a:first-child::after
        { content: counter(source-line);
          position: relative; left: -1em; text-align: right; vertical-align: baseline;
          border: none; display: inline-block;
          -webkit-touch-callout: none; -webkit-user-select: none;
          -khtml-user-select: none; -moz-user-select: none;
          -ms-user-select: none; user-select: none;
          padding: 0 4px; width: 4em;
          color: #aaaaaa;
        }
      pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa;  padding-left: 4px; }
      div.sourceCode
        {   }
      @media screen {
      pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
      }
      code span.al { color: #ff0000; font-weight: bold; } /* Alert */
      code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
      code span.at { color: #7d9029; } /* Attribute */
      code span.bn { color: #40a070; } /* BaseN */
      code span.bu { } /* BuiltIn */
      code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
      code span.ch { color: #4070a0; } /* Char */
      code span.cn { color: #880000; } /* Constant */
      code span.co { color: #60a0b0; font-style: italic; } /* Comment */
      code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
      code span.do { color: #ba2121; font-style: italic; } /* Documentation */
      code span.dt { color: #902000; } /* DataType */
      code span.dv { color: #40a070; } /* DecVal */
      code span.er { color: #ff0000; font-weight: bold; } /* Error */
      code span.ex { } /* Extension */
      code span.fl { color: #40a070; } /* Float */
      code span.fu { color: #06287e; } /* Function */
      code span.im { } /* Import */
      code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
      code span.kw { color: #007020; font-weight: bold; } /* Keyword */
      code span.op { color: #666666; } /* Operator */
      code span.ot { color: #007020; } /* Other */
      code span.pp { color: #bc7a00; } /* Preprocessor */
      code span.sc { color: #4070a0; } /* SpecialChar */
      code span.ss { color: #bb6688; } /* SpecialString */
      code span.st { color: #4070a0; } /* String */
      code span.va { color: #19177c; } /* Variable */
      code span.vs { color: #4070a0; } /* VerbatimString */
      code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
      div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
      pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
      table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
      th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
      td {padding: 2px 6px; border: 1px solid;}
      img {display: block; margin-left: auto; margin-right: auto;}
      figcaption {text-align: center;}
    </style>
  </head>
  <body style="padding-top: 70px;">
    <div class="container">
    <nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-primary">
      <div class="container-fluid">
        <span class="navbar-brand">Gtk4 tutorial</span>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
          <ul class="navbar-nav me-auto mb-2 mb-lg-0">
            <li class="nav-item">
<a class="nav-link" href="index.html">Home</a>
</li>

            <li class="nav-item">
<a class="nav-link" href="sec28.html">Prev: section28</a>
</li>

            <li class="nav-item">
<a class="nav-link" href="sec30.html">Next: section30</a>
</li>

          </ul>
        </div>
      </div>
    </nav>
<h1 id="gtkcolumnview">GtkColumnView</h1>
<h2 id="gtkcolumnview-1">GtkColumnView</h2>
<p>GtkColumnView is like GtkListView, but it has multiple columns. Each
column is GtkColumnViewColumn.</p>
<figure>
<img src="image/column_view.png" alt="Column View" />
<figcaption aria-hidden="true">Column View</figcaption>
</figure>
<ul>
<li>GtkColumnView has “model” property. The property points a
GtkSelectionModel object.</li>
<li>Each GtkColumnViewColumn has “factory” property. The property points
a GtkListItemFactory (GtkSignalListItemFactory or
GtkBuilderListItemFactory).</li>
<li>The factory connects GtkListItem and items of GtkSelectionModel. And
the factory builds the descendant widgets of GtkColumnView to display
the item on the display. This process is the same as the one in
GtkListView.</li>
</ul>
<p>The following diagram shows how it works.</p>
<figure>
<img src="image/column.png" alt="ColumnView" />
<figcaption aria-hidden="true">ColumnView</figcaption>
</figure>
<p>The example in this section is a window that displays information of
files in a current directory. The information is the name, size and last
modified datetime of files. So, there are three columns.</p>
<p>In addition, the example uses GtkSortListModel and GtkSorter to sort
the information.</p>
<h2 id="column.ui">column.ui</h2>
<p>Ui file specifies widgets and list item templates.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
column/column.ui @@@</p>
<ul>
<li>3-12: GtkApplicationWindow has a child widget GtkScrolledWindow.
GtkScrolledWindoww has a child widget GtkColumnView.</li>
<li>12-18: GtkColumnView has “model” property. It points
GtkSelectionModel interface. GtkNoSelection class is used as
GtkSelectionModel. And again, it has “model” property. It points
GtkSortListModel. This list model supports sorting the list. It will be
explained in the later subsection. And it also has “model” property. It
points GtkDirectoryList. Therefore, the chain is: GtkColumnView =&gt;
GtkNoSelection =&gt; GtkSortListModel =&gt; GtkDirectoryList.</li>
<li>18-20: GtkDirectoryList. It is a list of GFileInfo, which holds
information of files under a directory. It has “attributes” property. It
specifies what attributes is kept in each GFileInfo.
<ul>
<li>“standard::name” is a name of the file.</li>
<li>“standard::icon” is a GIcon object of the file</li>
<li>“standard::size” is the file size.</li>
<li>“time::modified” is the date and time the file was last
modified.</li>
</ul></li>
<li>29-79: The first GtkColumnViewColumn object. There are four
properties, “title”, “expand”, factory” and “sorter”.</li>
<li>31: Sets the “title” property to “Name”. This is the title on the
header of the column.</li>
<li>32: Sets the “expand” property to TRUE to allow the column to expand
as much as possible. (See the image above).</li>
<li>33- 69: Sets the “factory” property to GtkBuilderListItemFactory.
The factory has “bytes” property which holds a ui string to define a
template to build GtkListItem composite widget. The CDATA section (line
36-66) is the ui string to put into the “bytes” property. The contents
are the same as the ui file <code>factory_list.ui</code> in the section
27.</li>
<li>70-77: Sets the “sorter” property to GtkStringSorter object. This
object provides a sorter that compares strings. It has “expression”
property. A closure tag with a string type function
<code>get_file_name</code> is used here. The function will be explained
later.</li>
<li>80-115: The second GtkColumnViewColumn object. Its sorter property
is set to GtkNumericSorter.</li>
<li>116-151: The third GtkColumnViewColumn object. Its sorter property
is set to GtkNumericSorter.</li>
</ul>
<h2 id="gtksortlistmodel-and-gtksorter">GtkSortListModel and
GtkSorter</h2>
<p>GtkSortListModel is a list model that sorts its elements according to
a GtkSorter instance assigned to the “sorter” property. The property is
bound to “sorter” property of GtkColumnView in line 22 to 24.</p>
<div class="sourceCode" id="cb1"><pre
class="sourceCode xml"><code class="sourceCode xml"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkSortListModel&quot;</span><span class="ot"> id=</span><span class="st">&quot;sortlist&quot;</span>&gt;</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>... ... ...</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>  &lt;<span class="kw">binding</span><span class="ot"> name=</span><span class="st">&quot;sorter&quot;</span>&gt;</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">lookup</span><span class="ot"> name=</span><span class="st">&quot;sorter&quot;</span>&gt;columnview&lt;/<span class="kw">lookup</span>&gt;</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>  &lt;/<span class="kw">binding</span>&gt;</span></code></pre></div>
<p>Therefore, <code>columnview</code> determines the way how to sort the
list model. The “sorter” property of GtkColumnView is read-only property
and it is a special sorter. It reflects the user’s sorting choice. If a
user clicks the header of a column, then the sorter (“sorter” property)
of the column is referenced by “sorter” property of the GtkColumnView.
If the user clicks the header of another column, then the “sorter”
property of the GtkColumnView refers to the newly clicked column’s
“sorter” property.</p>
<p>The binding above makes a indirect connection between the “sorter”
property of GtkSortListModel and the “sorter” property of each
column.</p>
<p>GtkSorter compares two items (GObject or its descendant). GtkSorter
has several child objects.</p>
<ul>
<li>GtkStringSorter compares strings taken from the items.</li>
<li>GtkNumericSorter compares numbers taken from the items.</li>
<li>GtkCustomSorter uses a callback to compare.</li>
<li>GtkMultiSorter combines multiple sorters.</li>
</ul>
<p>The example uses GtkStringSorter and GtkNumericSorter.</p>
<p>GtkStringSorter uses GtkExpression to get the strings from the items
(objects). The GtkExpression is stored in the “expression” property of
the GtkStringSorter. When GtkStringSorter compares two items, it
evaluates the expression by calling <code>gtk_expression_evaluate</code>
function. It assigns each item to the second argument (‘this’ object) of
the function.</p>
<p>In the ui file above, the GtkExpression is in the line 71 to 76.</p>
<div class="sourceCode" id="cb2"><pre
class="sourceCode xml"><code class="sourceCode xml"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkStringSorter&quot;</span>&gt;</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>  &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;expression&quot;</span>&gt;</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">closure</span><span class="ot"> type=</span><span class="st">&quot;gchararray&quot;</span><span class="ot"> function=</span><span class="st">&quot;get_file_name&quot;</span>&gt;</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>    &lt;/<span class="kw">closure</span>&gt;</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>  &lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>&lt;/<span class="kw">object</span>&gt;</span></code></pre></div>
<p>The GtkExpression calls <code>get_file_name</code> function when it
is evaluated.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
column/column.c get_file_name @@@</p>
<p>The function is given the item (GFileInfo) of the GtkSortListModel as
an argument (<code>this</code> object). But you need to be careful that
it can be NULL while the list item is being recycled. So,
<code>G_IS_FILE_INFO (info)</code> is always necessary in callback
functions. The function retrieves a filename from <code>info</code>. The
string is owned by <code>info</code> so it is necessary to duplicate it.
And it returns the copied string.</p>
<p>GtkNumericSorter compares numbers. It is used in the line 106 to 112
and line 142 to 148. The lines from 106 to 112 is:</p>
<div class="sourceCode" id="cb3"><pre
class="sourceCode xml"><code class="sourceCode xml"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkNumericSorter&quot;</span>&gt;</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>  &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;expression&quot;</span>&gt;</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">closure</span><span class="ot"> type=</span><span class="st">&quot;gint64&quot;</span><span class="ot"> function=</span><span class="st">&quot;get_file_size&quot;</span>&gt;</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>    &lt;/<span class="kw">closure</span>&gt;</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>  &lt;/<span class="kw">property</span>&gt;</span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>  &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;sort-order&quot;</span>&gt;GTK_SORT_ASCENDING&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>&lt;/<span class="kw">object</span>&gt;</span></code></pre></div>
<p>The closure tag specifies a callback function
<code>get_file_size</code>.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
column/column.c get_file_size @@@</p>
<p>It just returns the size of <code>info</code>. The type of the size
is <code>goffset</code>. The type <code>goffset</code> is the same as
<code>gint64</code>.</p>
<p>The lines from 142 to 148 is:</p>
<div class="sourceCode" id="cb4"><pre
class="sourceCode xml"><code class="sourceCode xml"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkNumericSorter&quot;</span><span class="ot"> id=</span><span class="st">&quot;sorter_datetime_modified&quot;</span>&gt;</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>  &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;expression&quot;</span>&gt;</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">closure</span><span class="ot"> type=</span><span class="st">&quot;gint64&quot;</span><span class="ot"> function=</span><span class="st">&quot;get_file_unixtime_modified&quot;</span>&gt;</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    &lt;/<span class="kw">closure</span>&gt;</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>  &lt;/<span class="kw">property</span>&gt;</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>  &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;sort-order&quot;</span>&gt;GTK_SORT_ASCENDING&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>&lt;/<span class="kw">object</span>&gt;</span></code></pre></div>
<p>The closure tag specifies a callback function
<code>get_file_unixtime_modified</code>.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
column/column.c get_file_unixtime_modified @@@</p>
<p>It gets the modification date and time (GDateTime type) of
<code>info</code>. Then it gets a unix time from <code>dt</code>. Unix
time, sometimes called unix epoch, is the number of seconds that have
elapsed since 00:00:00 UTC on 1 January 1970. It returns the unix time
(gint64 type).</p>
<h2 id="column.c">column.c</h2>
<p><code>column.c</code> is as follows. It is simple and short thanks to
<code>column.ui</code>.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
column/column.c @@@</p>
<h2 id="compilation-and-execution.">Compilation and execution.</h2>
<p>All the source files are in <code>src/column</code> directory. Change
your current directory to the directory and type the following.</p>
<pre><code>$ meson _build
$ ninja -C _build
$ _build/column</code></pre>
<p>Then, a window appears.</p>
<figure>
<img src="image/column_view.png" alt="Column View" />
<figcaption aria-hidden="true">Column View</figcaption>
</figure>
<p>If you click the header of a column, then the whole lists are sorted
by the column. If you click the header of another column, then the whole
lists are sorted by the newly selected column.</p>
<p>GtkColumnView is very useful and it can manage very big GListModel.
It is possible to use it for file list, application list, database
frontend and so on.</p>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
  </body>
  </html>
